package com.exchange.service;

import com.exchange.entity.Merchant;
import com.exchange.repository.ExchangeCodeRepository;
import com.exchange.repository.MerchantRepository;
import com.exchange.repository.OrderRepository;
import com.exchange.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

@Service
public class ResourceLimitService {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private ExchangeCodeRepository exchangeCodeRepository;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private MerchantRepository merchantRepository;
    
    @Value("${resource-limits.free-tier.max-products:10}")
    private Integer defaultMaxProducts;
    
    @Value("${resource-limits.free-tier.max-exchange-codes:100}")
    private Integer defaultMaxExchangeCodes;
    
    @Value("${resource-limits.free-tier.max-orders-per-month:100}")
    private Integer defaultMaxOrdersPerMonth;
    
    public boolean isWithinProductLimit(Long merchantId, Integer maxAllowed) {
        Long currentCount = productRepository.countByMerchantId(merchantId);
        Integer maxProducts = getMaxForResource(merchantId, "products", maxAllowed);
        return currentCount < maxProducts;
    }
    
    public boolean isWithinExchangeCodeLimit(Long merchantId, Integer maxAllowed) {
        Long currentCount = exchangeCodeRepository.countByMerchantId(merchantId);
        Integer maxCodes = getMaxForResource(merchantId, "exchange_codes", maxAllowed);
        return currentCount < maxCodes;
    }
    
    public boolean isWithinOrderLimit(Long merchantId, Integer maxAllowed) {
        LocalDateTime startOfMonth = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth());
        Long currentCount = orderRepository.countByMerchantIdAndMonth(merchantId, startOfMonth);
        Integer maxOrders = getMaxForResource(merchantId, "orders", maxAllowed);
        return currentCount < maxOrders;
    }
    
    private Integer getMaxForResource(Long merchantId, String resourceType, Integer maxFromParam) {
        // If a specific max value is provided as parameter, use that
        if (maxFromParam != null) {
            return maxFromParam;
        }
        
        // Otherwise, get the merchant's specific limit from the database
        return merchantRepository.findById(merchantId)
                .map(merchant -> {
                    switch (resourceType) {
                        case "products":
                            return merchant.getMaxProducts();
                        case "exchange_codes":
                            return merchant.getMaxExchangeCodes();
                        case "orders":
                            return merchant.getMaxOrdersPerMonth();
                        default:
                            return defaultMaxProducts;
                    }
                })
                .orElseGet(() -> {
                    // If merchant not found, use default value based on resource type
                    switch (resourceType) {
                        case "products": return defaultMaxProducts;
                        case "exchange_codes": return defaultMaxExchangeCodes;
                        case "orders": return defaultMaxOrdersPerMonth;
                        default: return defaultMaxProducts;
                    }
                });
    }
    
    public ResourceUsage getUsageForMerchant(Long merchantId) {
        Long productCount = productRepository.countByMerchantId(merchantId);
        Long exchangeCodeCount = exchangeCodeRepository.countByMerchantId(merchantId);
        
        LocalDateTime startOfMonth = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth());
        Long orderCount = orderRepository.countByMerchantIdAndMonth(merchantId, startOfMonth);
        
        Merchant merchant = merchantRepository.findById(merchantId).orElse(null);
        
        Integer maxProducts = merchant != null ? merchant.getMaxProducts() : defaultMaxProducts;
        Integer maxExchangeCodes = merchant != null ? merchant.getMaxExchangeCodes() : defaultMaxExchangeCodes;
        Integer maxOrders = merchant != null ? merchant.getMaxOrdersPerMonth() : defaultMaxOrdersPerMonth;
        
        return new ResourceUsage(
            productCount, exchangeCodeCount, orderCount,
            maxProducts, maxExchangeCodes, maxOrders
        );
    }
    
    public static class ResourceUsage {
        private final Long currentProducts;
        private final Long currentExchangeCodes;
        private final Long currentOrders;
        private final Integer maxProducts;
        private final Integer maxExchangeCodes;
        private final Integer maxOrders;
        
        public ResourceUsage(Long currentProducts, Long currentExchangeCodes, Long currentOrders,
                            Integer maxProducts, Integer maxExchangeCodes, Integer maxOrders) {
            this.currentProducts = currentProducts;
            this.currentExchangeCodes = currentExchangeCodes;
            this.currentOrders = currentOrders;
            this.maxProducts = maxProducts;
            this.maxExchangeCodes = maxExchangeCodes;
            this.maxOrders = maxOrders;
        }
        
        // Getters
        public Long getCurrentProducts() { return currentProducts; }
        public Long getCurrentExchangeCodes() { return currentExchangeCodes; }
        public Long getCurrentOrders() { return currentOrders; }
        public Integer getMaxProducts() { return maxProducts; }
        public Integer getMaxExchangeCodes() { return maxExchangeCodes; }
        public Integer getMaxOrders() { return maxOrders; }
        
        public int getProductsPercentageUsed() {
            return maxProducts != 0 ? Math.min(100, (int) ((currentProducts * 100) / maxProducts)) : 0;
        }
        
        public int getExchangeCodesPercentageUsed() {
            return maxExchangeCodes != 0 ? Math.min(100, (int) ((currentExchangeCodes * 100) / maxExchangeCodes)) : 0;
        }
        
        public int getOrdersPercentageUsed() {
            return maxOrders != 0 ? Math.min(100, (int) ((currentOrders * 100) / maxOrders)) : 0;
        }
    }
}